(in-ns 'miked.problems)
(clojure/refer 'clojure)

(load-file "math.clj")
(load-file "grid.clj")
(load-file "util.clj")

(refer 'miked.math)

(refer 'miked.util)
(refer 'miked.grid)

(defn p1 [] 
  
  (defn findNums [x] 
    (if (> x 1000) 
      nil
      (if (or (divides x 3)
	      (divides x 5))
	(cons x (findNums (+ x 1)))
	(findNums (+ x 1)))))
  
  (apply + (findNums 1)))

(defn p2 [] 
  (apply + (filter even (take-while (fn [x] (<= x 1000000)) fib))))

(defn p3 []
  (factor 317584931803))

(defn p4 []
  (let [nums (range 100 999)]
    (apply max (filter (comp palindrome? digits) (cross-multiply nums nums)))))

(defn p6 []
  (let [sum-of-squares 
	(apply + (map square (range 1 101)))
	square-of-sums
	(square (apply + (range 1 101)))]
    (- square-of-sums sum-of-squares)))

(defn p7 []
  (nth (primes) 10000))

(defn p8 []
  (loop [res 0
	 x (digits 7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450)]
    (if x 
      (recur (max res (apply * (take 5 x))) (rest x))
      res)))

(defn p10 []
  (apply + (take-while (fn [x] (< x 1000000)) (primes))))

(def p11-numbers
     '(08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08
       49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00
       81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65
       52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91
       22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80
       24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50
       32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70
       67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21
       24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72
       21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95
       78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92
       16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57
       86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58
       19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40
       04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66
       88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69
       04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36
       20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16
       20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54
       01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48))

(def p11-grid  (grid (take 20 (repeat 20)) p11-numbers))

(defn line-prod [g p dir len]
  (apply * (filter identity (items-at g (grid-line p dir len)))))

(defn max-line-prod [g p len]
  (apply max (map (fn [dir] (line-prod g p dir len)) (list south east north-east south-east))))

(defn p11 [] (apply max (map (fn [row] (apply max row))  (grid-map (fn [g pt] (max-line-prod g pt 4)) p11-grid))))


(defn p36 []
  (apply + (filter (fn [x] 
		       (and (palindrome? (digits x))
			    (palindrome? (digits x 2))))
		   (range 1 1000000))))

(defn p23 []
  (let [limit 28124] 
    (let [abundants (filter abundant? (range 1 limit))
   	  bits (new java.util.BitSet)]
      (loop [xs abundants
		ys abundants]
	(when xs
	  (let [bit (+ (first xs) (first ys))]
	    (if (< bit limit)
	      (do (. bits (set bit))
		  (recur xs (rest ys)))
	      (recur (rest xs) abundants)))))
      (apply + (get-clear-bits bits (range 1 limit))))))

(defn p48 []
  (loop [sum 0 x 1]
    (if (> x 1000)
      sum
      (recur (rem (+ sum (pow x x)) 10000000000) (inc x)))))

(defn rotations [xs]
  (defn pivot [i] 
    (concat (drop i xs) (take i xs)))
  (def len (count xs))
  (defn rotation [i]
    (when (< i len)
      (lazy-cons (pivot i) (rotation (inc i)))))
  (rotation 0))

(defn int-rotations [x]
  (defn helper [high low]
    (when (< 1 high)
      (lazy-cons (+ (* (rem x high) low)
		    (quot x high))
		 (helper (quot high 10) (* low 10)))))
  (lazy-cons x (helper (get-exp x 10) 10)))

(defn circular-prime? [x] (every? prime? (int-rotations x)))

(defn p35[] 
  (count (filter circular-prime? (take-while (partial > 1000000) primes))))

(defn truncatable-prime? [x]
  (defn f [y] (and (not (== 1 y)) (prime? y)))
  (defn helper [exp]
    (let [q (quot x exp)
	  r (rem x exp)]
      (if (> 1 q)
	true
	(if (and (f q) (f r))
	  (helper (* exp 10))
	  false))))
  (helper 10))

(defn p37 []
  (take 11
	(filter truncatable-prime? (drop-while (partial > 10) primes))))



